package com.zx.zxapibackend.common;

import com.zx.zxapicommon.constant.RedisConstant;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @author 张旭
 * @version 1.0
 */
@Component
@Data
public class RedisTokenBucket {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 过期时间，400秒后过期
     */
    private static final long EMPIRE_TIME = 400;

    /**
     * 令牌桶算法，一分钟内每个手机号只能发送一次
     *
     * @param phoneNum
     * @return
     */
    public Boolean tryAcquire(String phoneNum) {

        //每个手机号一分钟内只能发送一条短信，也就是每分钟填充的令牌数
        int permitsPerMinute = 1;

        //令牌桶容量
        int maxPermits = 1;
        //key
        String key = RedisConstant.SMS_BUCKET_PREFIX + phoneNum;
        //获取当前时间戳
        long now = System.currentTimeMillis();
        //获取当前手机号对应的令牌桶中的令牌数
        int tokens = Integer.parseInt(redisTemplate.opsForValue().get(key + "tokens") == null ?
                "0" : String.valueOf(redisTemplate.opsForValue().get(key + "tokens")));
        //获取令牌桶上次填充的时间戳
        long lastRefillTime = Long.parseLong(redisTemplate.opsForValue().get(key + "last_refill_time") == null ?
                "0" : String.valueOf(redisTemplate.opsForValue().get(key + "last_refill_time")));
        //计算时间差
        long timeSinceLast = now - lastRefillTime;
        // 计算需要填充的令牌数,生成的令牌 = (当前时间-上次刷新时间)* 放入令牌的速率
        int refill = (int) (timeSinceLast / 1000 * permitsPerMinute / 60);

        //更新令牌桶内令牌数
        tokens = Math.min(tokens + refill, maxPermits);

        //更新上次时间戳
        redisTemplate.opsForValue().set(key + "last_refill_time", String.valueOf(now), EMPIRE_TIME, TimeUnit.SECONDS);

        //如果令牌数大于等于1，获取令牌
        if (tokens >= 1) {
            tokens--;
            redisTemplate.opsForValue().set(key + "tokens", String.valueOf(tokens), EMPIRE_TIME, TimeUnit.SECONDS);
            //获取到令牌，返回true
            return true;
        }
        //没有获取到令牌，返回false
        return false;
    }
}
